<!DOCTYPE html>
            <html lang="en" data-theme="light">

                <head>
                    <meta charset="utf-8">

                    <title>The Book of Shaders</title>
                    <link href="/favicon.gif" rel="shortcut icon" />
                    <meta name="keywords" content="shader,openGL,WebGL,GLSL,book,procedural,generative" />
                    <meta name="description" content="Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders." />

                    <!— Open Graph data —>
                    <meta property="og:type" content="article" />
                    <meta property="og:title" content="The Book of Shaders" />
                    <meta property="og:site_name" content="The Book of Shaders" />
                    <meta property="og:description" content="Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders." />
                    <meta property="og:image" content="http://thebookofshaders.com/thumb.png" />
                    <meta property="og:image:secure_url" content="https://thebookofshaders.com/thumb.png" />
                    <meta property="og:image:type" content="image/png" />
                    <meta property="og:image:width" content="500" />
                    <meta property="og:image:height" content="500" />

                    <link href="/favicon.gif" rel="shortcut icon" />

                    <!-- Highlight -->
                    <link type="text/css" rel="stylesheet" href="/css/github.css">
                    <script type="text/javascript" src="/src/highlight.min.js"></script>
                    <!-- GlslCanvas -->
                    <script type="text/javascript" src="/src/glslCanvas/GlslCanvas.js"></script>

                    <!-- GlslEditor -->
                    <link type="text/css" rel="stylesheet" href="/src/glslEditor/glslEditor.css">
                    <script type="application/javascript" src="/src/glslEditor/glslEditor.js"></script>

                    <!-- GlslGallery -->
                    <link type="text/css" rel="stylesheet" href="/src/glslGallery/glslGallery.css">
                    <script type="application/javascript" src="/src/glslGallery/glslGallery.js"></script>

                    <!-- Main style -->
                    <link type="text/css" rel="stylesheet" href="/css/styles.css">

                </head>

                <body>
                    <div class="toc-header">
                        <p class="subtitle"><a href="https://thebookofshaders.com/">The Book of Shaders</a> by <a href="http://patriciogonzalezvivo.com">Patricio Gonzalez Vivo</a> & <a href="http://jenlowe.net">Jen Lowe</a> </p>
                        <p>  <a href=".">中文版</a> - <a href="https://thebookofshaders.com/">English</a></p>
                    </div>
                    <hr>

                    <div id="content"><h1 id="图像处理">图像处理</h1>
<h2 id="纹理">纹理</h2>
<p><img src="01.jpg" alt=""></p>
<p>显卡（GPU）有特殊的图像存储类型。在中央处理器（CPU）上，图像往往储存成字节数组，但在GPU上图像却往往储存成<code>sampler2D</code> （二维采样器）。它们更像是一个由浮点向量组成的表格（或矩阵）。更有意思的是，这张<em>表格</em>中向量们的值是连续的。这意味着邻近像素点的值之间是用较低级别的插值出来的。</p>
<p>为了用到这一特性，我们首先要把图像从CPU<em>上传</em>到GPU，然后再把纹理的<code>id</code>（序列号）传给对应的<a href="../05"><code>uniform</code></a>。这一切流程发生在着色器之外。</p>
<p>一旦纹理（texture）加载好了并且链接上了一个有效的<code>uniform sampler2D</code>，你就可以用<a href="index.html#texture2D.md"><code>texture2D()</code></a>函数获取特定坐标（用二维向量<a href="index.html#vec2.md"><code>vec2</code></a>类型表示）对应的颜色值（用四维向量<a href="index.html#vec4.md"><code>vec4</code></a>类型表示）。</p>
<pre><code class="language-glsl">vec4 texture2D(sampler2D texture, vec2 coordinates)  
</code></pre>
<p>在下面的代码中，我们将《神奈川冲浪里》（1830）以<code>uniform sampler2D u_tex0</code>为类型和名字加载了进来，并且我们在显示平面（billboard）中调用了它的每个像素：</p>
<div class="codeAndCanvas" data="texture.frag" data-textures="hokusai.jpg"></div>

<p>如果你注意观察，你会发现纹理的坐标是归一化的！这真的是个惊喜，不是吗？纹理坐标和我们已然熟识的东西是一致的。它们的坐标总是在0.0和1.0之间。这意味着它和我们使用的归一化空间坐标完美吻合。</p>
<p>现在既然你已经了解了如何正确的加载纹理，是时候来试验性地探索我们究竟能用这一技巧做些什么了。快试试下面这些：</p>
<ul>
<li>把纹理缩小到原先的一半。</li>
<li>把原先的纹理旋转90度。</li>
<li>将鼠标位置赋值给纹理坐标来移动纹理。</li>
</ul>
<p>为什么你要为纹理痴狂呢？首先请忘了通道那可悲的255值吧；一旦你的图像被转换成了一个<code>uniform sampler2D</code>，所有的值都会在0.0和1.0的区间范围内（小数精度取决于你把<code>precision</code>设置成了多少）。这就是着色器能创造出美轮美奂的后处理效果的原因。</p>
<p>其次，<a href="index.html#vec2.md"><code>vec2()</code></a>类型意味着你甚至可以获取到像素之间的值。如前所述，纹理是连续体（continuum）。也就是说，如果正确地设置好了你的纹理，你可以获取纹理图像上的任意值，而这些值在像素点之间是平滑过渡的！</p>
<p>最后，你可以让你的图像在边缘处重复。这样的话，在你获取坐标超出0.0到1.0这个归一化区间的值时，返回值会从另一边的边缘开始循环往复。</p>
<p>所有这些功能会让你的图像越发像是无限循环往复的化纤面料。你可以拉扯、伸缩你的纹理而无需注意它们原初的数据究竟是什么样的。要体验这一点，看看下面的代码吧，其中我们使用<a href="../11/">我们先前创建的噪声函数</a>来扭曲纹理。</p>
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>

<h2 id="纹理分辨率">纹理分辨率</h2>
<p>上述的种种示例仅展现了长宽相等的方形图像匹配方形显示平面（billboard）的情形。而至于非正方形图像，事情就没那么简单了。不幸的是，几个世纪以来的绘画艺术和摄影艺术发现非正方形比例的图像更令人赏心悦目。</p>
<p><img src="nicephore.jpg" alt="Joseph Nicéphore Niépce (1826)"></p>
<p>我们该如何解决这个问题呢？我们需要知道这一图像的原始比例，才好在放大或缩小纹理的时候正确地保持它原始的<a href="http://en.wikipedia.org/wiki/Aspect_ratio"><em>宽高比</em></a>。为此，纹理的宽和高是以<code>uniform</code>的形式传递进着色器的——在我们的示例框架里是以纹理名后跟<code>Resolution</code>的<code>uniform vec2</code>传递进来的。一旦着色器里有了这些信息，我们就可以将纹理分辨率的<code>宽度</code>除以<code>高度</code>来获得纹理的宽高比。最后，把这个比例和<code>y</code>坐标相乘，我们就可以实现伸缩这根轴来让纹理匹配其原始比例。</p>
<p>取消 21 行的注释来实操一下吧。</p>
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>

<ul>
<li>如果要这张图片居中显示，我们应该怎么做呢？</li>
</ul>
<h2 id="数字装潢（digital-upholstery）">数字装潢（Digital upholstery）</h2>
<p><img src="03.jpg" alt=""></p>
<p>你可能会觉得这有些不必要地繁琐……呃，可能你是对的。但这种对待图像的方式给了“奇技淫巧”足够的发挥空间。试想，你是一名室内装潢师，通过拉伸和折叠织物结构，你可以创造出更好的新图案和新技巧。</p>
<p><img src="muybridge.jpg" alt="Eadweard&#39;s Muybridge study of motion"></p>
<p>这种水平的工艺可以追溯到最早的一些光学实验。例如，游戏里很常见的<em>精灵动画</em>（<em>sprite animations</em>），你不可避免地会在它身上看到费纳奇镜（phenakistoscope）、西洋镜（zoetrope）和改良版西洋镜（praxinoscope）的影子。</p>
<p>这看起来很简单，但修改纹理坐标可以带来巨大的可能性。例如：</p>
<div class="codeAndCanvas" data="texture-sprite.frag" data-textures="muybridge.jpg"></div>

<p>现在轮到你了:</p>
<ul>
<li><p>你能活学活用，做出万花筒效果吗？</p>
</li>
<li><p>在Oculus和谷歌Cardboard之前，立体摄影是件大事。你能编写一个简单的着色器来重新使用这些美丽的图像吗?</p>
</li>
</ul>
<p><a href=“../edit.php#10/ikeda-03.frag”><canvas id=“custom” class=“canvas” data-fragment-url=“ikeda-03.frag”  width=“520px” height=“200px”></canvas></a></p>
<ul>
<li>你还可以使用纹理来再创造哪些其他的光学玩具吗?</li>
</ul>
<p>在下一章中，我们将学习如何使用着色器进行一些图像处理。你会注意到着色器的复杂性最终是有意义的，因为它很大程度上是为这一过程而生的。我们将开始做一些图像操作!</p>
</div>

                    <hr>
                    <ul class="navigationBar" >
                            <li class="navigationBar" onclick="previusPage()">&lt; &lt; Previous</li>
                            <li class="navigationBar" onclick="homePage()"> Home </li>
                            <li class="navigationBar" onclick="nextPage()">Next &gt; &gt;</li>
                        </ul>

                    <footer>
                        <p> Copyright 2015 <a href="http://www.patriciogonzalezvivo.com" target="_blank">Patricio Gonzalez Vivo</a> </p>
                    </footer>

                    <script src="/src/three.min.js"></script>

                    <script id="vertexShader" type="x-shader/x-vertex">
                        void main() {
                            gl_Position = vec4( position, 1.0 );
                        }
                    </script>

                    <script id="fragmentShader" type="x-shader/x-fragment">
                        uniform vec2 u_resolution;
                        uniform vec2 u_mouse;
                        uniform float u_time;

                        void main() {
                            vec2 st = gl_FragCoord.xy/u_resolution.xy;
                            gl_FragColor=vec4(st.x,st.y,0.0,1.0);
                        }
                    </script>

                    <script>
                        var camera, scene, renderer, clock;
                        var uniforms;
                        var mouse = { x: 0, y: 0 };

                        document.onmousemove = getMouseXY;

                        function getMouseXY (e) {
                            mouse.x = e.pageX;
                            mouse.y = e.pageY;
                        }

                        function init () {

                            camera = new THREE.Camera();
                            camera.position.z = 1;

                            scene = new THREE.Scene();
                            clock = new THREE.Clock();

                            var geometry = new THREE.PlaneBufferGeometry(2, 2);

                            uniforms = {
                                u_time: { type: "f", value: 1.0 },
                                u_mouse: { type: "v2", value: new THREE.Vector2() },
                                u_resolution: { type: "v2", value: new THREE.Vector2() }
                            };

                            var material = new THREE.ShaderMaterial({
                                uniforms: uniforms,
                                vertexShader: document.getElementById('vertexShader').textContent,
                                fragmentShader: document.getElementById('fragmentShader').textContent
                            });

                            var mesh = new THREE.Mesh(geometry, material);
                            scene.add(mesh);

                            renderer = new THREE.WebGLRenderer();
                            renderer.setPixelRatio(window.devicePixelRatio);

                            container.appendChild(renderer.domElement);

                            onWindowResize();
                            window.addEventListener('resize', onWindowResize, false);
                        }

                        function onWindowResize (event) {
                            renderer.setSize(window.innerWidth, window.innerHeight);
                            uniforms.u_resolution.value.x = renderer.domElement.width;
                            uniforms.u_resolution.value.y = renderer.domElement.height;
                            uniforms.u_mouse.value.x = mouse.x;
                            uniforms.u_mouse.value.y = mouse.y;
                        }

                        function animate () {
                            requestAnimationFrame(animate);
                            render();
                        }

                        function render () {
                            uniforms.u_time.value += clock.getDelta();
                            renderer.render(scene, camera);
                        }

                        var container = document.getElementById('container');

                        if (container) {

                            init();
                            animate();
                        }
                    </script>
                    <script type="text/javascript" src="/src/main.js" defer></script>
                </body>

            </html>